﻿Imports System.Text

Public Class TCPServer

#Region " Declarations "
    Private myListener As Net.Sockets.TcpListener
    Private myListenThread As Threading.Thread
    Private myClients As New List(Of ConnectedClient) 'This list will store all connected clients.
    Private _WaitHandle_ClientComplete As New System.Threading.AutoResetEvent(False)
#End Region

#Region " Properties "

    Public ReadOnly Property GetClients As List(Of ConnectedClient)
        Get
            Return myClients
        End Get
    End Property

#End Region

#Region " Initialization "

    Public Sub StartServer()
        myListener = New System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 43001) 'The TcpListener will listen for incoming connections at port 43001
        myListener.Start() 'Start listening.
        myListenThread = New System.Threading.Thread(AddressOf doListen) 'This thread will run the doListen method
        myListenThread.IsBackground = True 'Since we dont want this thread to keep on running after the application closes, we set isBackground to true.
        myListenThread.Start() 'Start executing doListen on the worker thread.
    End Sub

#End Region

#Region " Destruction "

    ''' <summary>
    ''' Completely cleans up server resources
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Close()
        Try
            For Each client In myClients
                client.Close()
            Next
        Catch ex As Exception
            'objects are probably already disposed
        End Try
        Try
            myListener.Stop()
            myListenThread.Abort()
        Catch ex As Exception
            'object is probably already disposed
        End Try
    End Sub

    ''' <summary>
    ''' Removes a single client connection from server
    ''' </summary>
    ''' <param name="client"></param>
    ''' <remarks></remarks>
    Public Sub RemoveClient(ByVal client As ConnectedClient)
        If myClients.Contains(client) Then
            Try
                client.Close()
            Catch
                'client is probably already closed
            End Try
            myClients.Remove(client)
        End If
        'TODO update the gui remotely if a client is removed
    End Sub

#End Region

#Region " Read/Send Functions "

    Private Sub doListen()
        Dim incomingClient As System.Net.Sockets.TcpClient
        While myClients.Count < 4 And Not EndServerConnectListener
            incomingClient = myListener.AcceptTcpClient 'Accept the incoming connection. This is a blocking method so execution will halt here until someone tries to connect.
            Dim connClient As New ConnectedClient(incomingClient, Me) 'Create a new instance of ConnectedClient (check its constructor to see whats happening now).
            AddHandler connClient.dataReceived, AddressOf Me.messageReceived
            myClients.Add(connClient) 'Adds the connected client to the list of connected clients.
        End While
    End Sub

    Public Sub SendAllMessage(ByVal message As String)
        For Each connClient As ConnectedClient In myClients
            connClient.SendMessage(message)
        Next
    End Sub

    Public Sub SendAllMessageAsync(ByVal message As String)
        For Each connClient As ConnectedClient In myClients
            connClient.SendMessageAsync(message)
        Next
    End Sub

#End Region

#Region " Deserialization "

    Private Sub messageReceived(ByVal sender As ConnectedClient, ByVal message As String)
        'A message has been received from one of the clients.
        'To determine who its from, use the sender object.
        'sender.SendMessage can be used to reply to the sender.

        Dim data() As String = message.Split("|"c) 'Split the message on each | and place in the string array.
        Select Case data(0)
            Case "CONNECT"
                'We use GetClientByName to make sure no one else is using this username.
                'It will return Nothing if the username is free.
                'Since the client sent the message in this format: CONNECT|UserName, the username will be in the array on index 1.
                If GetClientByName(data(1)) Is Nothing Then
                    'The username is not taken, we can safely assign it to the sender.
                    sender.Username = data(1)
                    sender.Position = myClients.IndexOf(sender).ToString
                    sender.SendMessageAsync("RESPONSE|UPDATE|lblConnection|Connected to server succesfully!")
                    Dim strB As New StringBuilder("PUSH|UPDATE|lstTablePlayers|")
                    For Each client As ConnectedClient In myClients
                        strB.Append(client.Username)
                        strB.Append(";")
                    Next
                    strB.Remove(strB.Length - 1, 1) 'remove last separator
                    SendAllMessageAsync(strB.ToString)

                    SendAllMessageAsync("PUSH|UPDATE|lblPlayersJoined|" & myClients.Count.ToString)
                Else
                    'Client with this name is already connected, need to rename
                    sender.SendMessageAsync("RESPONSE|PROMPT|txtUsername")
                End If

                ''Check if all Clients are connected, start the game when done
                'If myClients.Count = 4 Then
                '    StartGameWithFour()
                'End If
            Case "CLOSE"
                Select Case data(1)
                    Case "Connection"
                        removeClient(sender)
                End Select
            Case "GET"
                Select Case data(1)
                    Case "Position"
                        sender.SendMessageAsync("RESPONSE|UPDATE|Position|" & sender.Position)
                End Select
            Case "CLIENT_FINISHED"
                '_WaitHandle_ClientComplete.Set()
        End Select

    End Sub

#End Region

#Region " Private Helper Functions "

    Private Function GetClientByName(ByVal name As String) As ConnectedClient
        For Each cc As ConnectedClient In myClients
            If cc.Username = name Then
                Return cc 'client found, return it.
            End If
        Next
        'If we've reached this part of the method, there is no client by that name
        Return Nothing
    End Function

    Private Sub StartGameWithFour()
        For Each client In myClients
            client.SendMessageAsync("START")
        Next
    End Sub

#End Region

End Class
